package auto.app.service.impl;

import auto.app.config.RestUtil;
import auto.app.controller.from.*;
import auto.app.controller.from.BeforeAssertion.BeforeAssertionChickFrom;
import auto.app.controller.info.BeforeAssertion.BeforeAssertionChickInfo;
import auto.app.controller.info.CaseDaoDetailInfo;
import auto.app.controller.info.CasePageInfo;
import auto.app.dao.ParamsDao;
import auto.app.exetion.MainExection;
import auto.app.model.*;
import auto.app.dao.CaseDao;
import auto.app.service.*;
import auto.app.util.CommonUtil;
import auto.app.util.MysqlUtil;
import auto.app.util.ObjectNull;
import auto.app.util.UserGetAccount;
import auto.app.vo.MysqlReturn;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

import static auto.app.exetion.MainExetionMessage.*;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 杨森
 * @since 2022-07-09
 */
@Service
@Slf4j
public class CaseServiceImpl extends ServiceImpl<CaseDao, CaseInterface> implements CaseService {
    @Autowired
    private CaseDao caseDao;

    @Autowired
    private ParamsDao paramsDao;

    @Autowired
    private ParamsService paramsService;

    @Autowired
    private RestUtil restUtil;

    @Autowired
    private ResultService resultService;

    @Autowired
    private InterfaceService interfaceService;

    @Autowired
    private AutoGlobalService autoGlobalService;

    @Autowired
    private BeforeAssertionService beforeAssertionService;

    @Autowired
    private CollectionCaseService collectionCaseService;

    @Autowired
    private DatabaseConnectService databaseConnectService;

    @Autowired
    private MysqlUtil mysqlUtil;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String callCase(CallCasePO callCasePO) throws MainExection {
        //根据case_id 判断用例是否存在
        CaseInterface aCaseInterface = baseMapper.selectById(callCasePO.getCaseId());
        if (ObjectUtil.isNull(aCaseInterface)) throw new MainExection(CASEERROR.getCode(), CASEERROR.getMsg());
        //用例存在 查询相关信息
        CaseDaoDto dtoByCaseId = caseDao.findDtoByCaseId(callCasePO.getCaseId());
        //校验数据
        if(ObjectUtil.isEmpty(dtoByCaseId.getParameterType()) || ObjectUtil.isEmpty(dtoByCaseId.getRequsetMethod())
                || ObjectUtil.isEmpty(dtoByCaseId.getRequestPath()) || ObjectUtil.isEmpty(dtoByCaseId.getIp())){
            throw new MainExection(CASEINTERFACEERROR.getCode(), CASEINTERFACEERROR.getMsg());
        }
        //数据校验通过 对ip和端口 路径进行组装存在ip中
        if (ObjectUtil.isNotNull(dtoByCaseId.getPort())) {
            dtoByCaseId.setIp(dtoByCaseId.getIp() + ":" + dtoByCaseId.getPort() + dtoByCaseId.getRequestPath());
        } else {
            dtoByCaseId.setIp(dtoByCaseId.getIp() + dtoByCaseId.getRequestPath());
        }
        //地址组装通过获取请求参数 请求体
        List<Params> paramsList = paramsDao.selectList(new QueryWrapper<Params>().eq("case_id", dtoByCaseId.getCaseId()));
        List<Params> paramHeader=new ArrayList<>();
        List<Params> paramsBody=new ArrayList<>();
        List<Params> urlParams =new ArrayList<>();
        for (Params params : paramsList) {
            if (ObjectUtil.isNull(params.getType())){
                log.info("测试用例ID为"+callCasePO.getCaseId()+"的参数params:"+params.getId()+"无类型");
            }
            if (params.getType().equals("H")){
                paramHeader.add(params);
            }
            if (params.getType().equals("B")){
                paramsBody.add(params);
            }
            //如果是url传参 需要提取出来
            if (dtoByCaseId.getUrlParameterTransfer() == 1){
                if (params.getType().equals("C")){
                    urlParams.add(params);
                }
            }
        }
        //获取请求方式
        HttpMethod method = CommonUtil.getMethod(dtoByCaseId.getRequsetMethod());
        //判断请求头 和请求头是否为空 非空的存入map集合
        MultiValueMap<String, String> hashMapHeader = new LinkedMultiValueMap<>();
        HashMap<String, Object> hashMapBody = new HashMap<>();
        //进行全局参数设置 查询出全部启用的参数
        List<AutoGlobal> autoGlobals = autoGlobalService.autoGlobalListCase();
        for (AutoGlobal autoGlobal : autoGlobals) {
            //如果是H就加入请求头
            if (autoGlobal.getParameType().equals("H")) {
                hashMapHeader.add(autoGlobal.getParameName(), autoGlobal.getDefaultValue());
            } else if (autoGlobal.getParameType().equals("B")) {
                //如果是请求体就加入请求体
                hashMapBody.put(autoGlobal.getParameName(), autoGlobal.getDefaultValue());
            }
        }
        //若全局参数和单个测试用例中的参数重合 优先使用全局参数
        if (paramHeader.size() > 0) {
            paramHeader.forEach(params -> {
                //判断是否存在相同的参数名的全局参数
                List<String> strings = hashMapHeader.get(params.getParamName());
                //如果存在则数组的长度大于0 若存在相同的 优先使用全局参数
                if (CollectionUtils.isEmpty(strings)) {
                    hashMapHeader.add(params.getParamName(), params.getParamValue());
                }
            });
        }
        //根据请求参数是否为数组 走不同的逻辑 赋值进入请求体中
        if (paramsBody.size() > 0) {
            for (Params params : paramsBody) {
                //判断是否存在相同的参数名的全局参数
                Object o = hashMapBody.get(params.getParamName());
                if (ObjectUtil.isNull(o)) {
                    if (params.getParamType() == 0) {
                        hashMapBody.put(params.getParamName(), params.getParamValue());
                    } else {
                        hashMapBody.put(params.getParamName(), JSONUtil.parseArray(params.getParamValue()));
                    }
                }
            }
        }
        //存入集合后进行参数类型判断 json 或者 form表单
        HashMap<String, String> type = CommonUtil.getType(dtoByCaseId.getParameterType());
        for (String key : type.keySet()) {
            hashMapHeader.add(key, type.get(key));
        }
        //进行接口调用
        try {
            ResponseEntity<String> execute = null;
            //判断是否url传参
            if (dtoByCaseId.getUrlParameterTransfer() == 1) {
                if (CollectionUtils.isEmpty(urlParams)) {
                    throw new MainExection(URLPARAMSISNULL.getCode(), URLPARAMSISNULL.getMsg());
                }
                StringBuilder urlIp = new StringBuilder(dtoByCaseId.getIp());
                for (int i = 0; i < urlParams.size(); i++) {
                    urlIp.append("/").append(urlParams.get(i).getParamValue());
                }
                String urlString = urlIp.toString();
                execute = restUtil.execute(hashMapBody, urlString, method, hashMapHeader);
            } else {
                //执行成功无异常录入执行结果
                execute = restUtil.execute(hashMapBody, dtoByCaseId.getIp(), method, hashMapHeader);
            }
            //断言
            List<BeforeAssertionChickInfo> beforeAssertionChickInfos = beforeAssertionService.beforeAssertionChick(BeforeAssertionChickFrom.builder().caseId(callCasePO.getCaseId()).jsonString(execute.getBody()).build());
            //无断言
            if (CollectionUtils.isEmpty(beforeAssertionChickInfos)) {
                Result result = Result.builder().caseId(callCasePO.getCaseId()).createDate(LocalDateTime.now()).status("S").result(execute.getBody()).message("执行成功").build();
                resultService.addResult(result);
            } else {
                AtomicReference<String> message = new AtomicReference<>("");
                beforeAssertionChickInfos.forEach(beforeAssertionChickInfo -> {
                    if (beforeAssertionChickInfo.getResult()) {
                        log.info(beforeAssertionChickInfo.getResult() + beforeAssertionChickInfo.getMsg());
                    } else {
                        log.info(beforeAssertionChickInfo.getResult() + beforeAssertionChickInfo.getMsg());
                        message.set(message + beforeAssertionChickInfo.getMsg());
                    }
                });
                String s = message.toString();
                if (ObjectUtils.isNotEmpty(s)) {
                    Result result = Result.builder().caseId(callCasePO.getCaseId()).createDate(LocalDateTime.now()).status("F").result(execute.getBody()).message(s).build();
                    resultService.addResult(result);
                } else {
                    Result result = Result.builder().caseId(callCasePO.getCaseId()).createDate(LocalDateTime.now()).status("S").message("执行成功").result(execute.getBody()).build();
                    resultService.addResult(result);
                }
            }
            try {
                //后置操作执行MYSQL
                List<DatabaseConnect> databaseConnects = databaseConnectService.listDatabaseConnectState(callCasePO.getCaseId());
                if (ObjectUtil.isNotNull(databaseConnects) && databaseConnects.size()>0){
                    for (DatabaseConnect databaseConnect : databaseConnects) {
                        MysqlReturn mysqlReturn = mysqlUtil.excult(databaseConnect.getDatabaseId());
                        log.info(String.valueOf(mysqlReturn));
                    }
                }
            }catch (Exception e){
                log.info("后置操作mysql执行报错,请检查sql");
                log.info(e.getMessage());
            }
            return "执行成功";
        } catch (Exception e) {
            //调用接口报错
            log.info(e.getMessage());
            Result result = Result.builder().caseId(callCasePO.getCaseId()).createDate(LocalDateTime.now()).status("F").result("执行失败").message(e.getMessage()).build();
            resultService.addResult(result);
            return "执行失败";
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean casegeneration(Integer caseId) throws MainExection {
        //根据case_id 判断用例是否存在
        CaseInterface aCaseInterface = baseMapper.selectById(caseId);
        if (ObjectUtil.isNull(aCaseInterface)) throw new MainExection(CASEERROR.getCode(), CASEERROR.getMsg());
        //用例存在 查询相关信息
        CaseDaoDto dtoByCaseId = caseDao.findDtoByCaseId(aCaseInterface.getCaseId());
        //地址组装通过获取请求参数 请求体
        List<Params> paramsList = paramsDao.selectList(new QueryWrapper<Params>().eq("case_id", dtoByCaseId.getCaseId()));
        if (CollectionUtils.isEmpty(paramsList)||paramsList.size()==0){
            throw new MainExection(PARAMSBODYERROR.getCode(),PARAMSBODYERROR.getMsg());
        }
        List<Params> paramHeader=new ArrayList<>();
        List<Params> paramsBody=new ArrayList<>();
        List<Params> urlParams =new ArrayList<>();
        for (Params params : paramsList) {
            if (ObjectUtil.isNull(params.getType())){
                log.info("测试用例ID为"+caseId+"的参数params:"+params.getId()+"无类型");
            }
            if (params.getType().equals("H")){
                paramHeader.add(params);
            }
            if (params.getType().equals("B")){
                paramsBody.add(params);
            }
            //如果是url传参 需要提取出来
            if (dtoByCaseId.getUrlParameterTransfer() == 1){
                if (params.getType().equals("C")){
                    urlParams.add(params);
                }
            }
        }
        //循环生成不传的测试用例
        for (Params params : paramsBody) {
            Map<String, Params> stringParamsMap = paramsBody.stream().collect(Collectors.toMap(Params::getParamName, Function.identity()));
            String paramName = params.getParamName();
            CaseInterface caseInterface = CaseInterface.builder().caseName(aCaseInterface.getCaseName() + paramName + "不传")
                    .createBy(UserGetAccount.getAccountId())
                    .createName(UserGetAccount.getAccountName())
                    .createTime(LocalDateTime.now())
                    .interfaceId(aCaseInterface.getInterfaceId())
                    .remark(aCaseInterface.getRemark()).build();
            baseMapper.insert(caseInterface);
            stringParamsMap.remove(params.getParamName());
            List<Params> paramsBodyNew=new ArrayList<>(stringParamsMap.values());
            ArrayList<Params> paramsArrayList = new ArrayList<>();
            paramsArrayList.addAll(paramHeader);
            paramsArrayList.addAll(urlParams);
            paramsArrayList.addAll(paramsBodyNew);
            paramsArrayList.forEach(params1 -> params1.setCaseId(caseInterface.getCaseId()));
            boolean saveBatch = paramsService.saveBatch(paramsArrayList);
        }

        return true;

    }

    @Override
    public CasePageInfo list(CasePageFrom casePageFrom) {
        //当传入的接口名称不为空时进行判断且附值
        if (ObjectUtils.isNotEmpty(casePageFrom.getInterfaceName())) {
            casePageFrom.setInterfaceName("%" + casePageFrom.getInterfaceName() + "%");
        }
        //自定义sql 分页
        Page<CaseDaoDto> page = new Page<>(casePageFrom.getCurrent(), casePageFrom.getSize());
        Page<CaseDaoDto> page1 = caseDao.listAll(page, casePageFrom);
        CasePageInfo casePageInfo = new CasePageInfo();
        casePageInfo.setTotal(page1.getTotal());
        casePageInfo.setCurrent(page1.getCurrent());
        casePageInfo.setSize(page1.getSize());
        casePageInfo.setRecords(page1.getRecords());
        return casePageInfo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean caseAddFrom(CaseAddFrom caseAddFrom) throws MainExection {
        //先判断接口信息是否存在
        Interface interfaceServiceById = interfaceService.getById(caseAddFrom.getInterfaceId());
        if (interfaceServiceById == null) {
            throw new MainExection(INTERFACEERROR.getCode(), INTERFACEERROR.getMsg());
        }
        //对用例名称进行判断是否重复
        LambdaQueryWrapper<CaseInterface> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(CaseInterface::getCaseName, caseAddFrom.getCaseName())
                .last("limit 1");
        CaseInterface one = this.getOne(queryWrapper);
        if (ObjectUtil.isNotNull(one)) {
            throw new MainExection(CASENAMEERROR.getCode(), CASENAMEERROR.getMsg());
        }
        //对参数进行设置且入库
        CaseInterface caseInterface = CommonUtil.copyOne(caseAddFrom, CaseInterface.class);
        caseInterface.setCreateTime(LocalDateTime.now());
        caseInterface.setCaseId(UserGetAccount.getAccountId());
        caseInterface.setCreateName(UserGetAccount.getAccountName());
        //对数据进行入库
        try {
            return save(caseInterface);
        } catch (Exception e) {
            throw new MainExection(CASEADDMYSQLERROR.getCode(), CASEADDMYSQLERROR.getMsg());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteList(CaseDeleteListFrom caseDeleteListFrom) throws MainExection {
        List<Integer> caseIdList = caseDeleteListFrom.getCaseIdList();
        if (caseIdList.size() == 0) {
            throw new MainExection(CASEDELETELISTERROR.getCode(), CASEDELETELISTERROR.getMsg());
        }
        //判断是否被测试合集使用中 在测试合集中使用 则不能删除
        LambdaQueryWrapper<CollectionCase> lambdaQueryWrapperCollectionCase = new LambdaQueryWrapper<>();
        lambdaQueryWrapperCollectionCase.in(CollectionCase::getCaseId, caseDeleteListFrom.getCaseIdList());
        List<CollectionCase> list1 = collectionCaseService.list(lambdaQueryWrapperCollectionCase);
        if (ObjectNull.isNotNull(list1)) {
            throw new MainExection(CASEDELETELISTCOLLECTCASEERROR.getCode(), CASEDELETELISTCOLLECTCASEERROR.getMsg());
        }
        //删除关联参数
        LambdaQueryWrapper<Params> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.in(Params::getCaseId, caseIdList);
        List<Params> paramsList = paramsDao.selectList(lambdaQueryWrapper);
        List<Integer> collect = paramsList.stream().map(Params::getId).collect(Collectors.toList());
        if (paramsList.size() > 0) {
            try {
                int deleteBatchIds = paramsDao.deleteBatchIds(collect);
            } catch (Exception e) {
                throw new MainExection(CASEDELETEPARMSERROR.getCode(), CASEDELETEPARMSERROR.getMsg());
            }
        }
        //删除结果记录
        LambdaQueryWrapper<Result> resultLambdaQueryWrapper = new LambdaQueryWrapper<>();
        resultLambdaQueryWrapper.in(Result::getCaseId, caseIdList);
        List<Result> list = resultService.list(resultLambdaQueryWrapper);
        List<Integer> collect1 = list.stream().map(Result::getId).collect(Collectors.toList());
        if (list.size() > 0) {
            try {
                boolean b = resultService.removeByIds(collect1);
            } catch (Exception e) {
                throw new MainExection(CASEDELETERESULTERROR.getCode(), CASEDELETERESULTERROR.getMsg());
            }
        }
        //删除测试用例
        try {
            boolean removeByIds = this.removeByIds(caseDeleteListFrom.getCaseIdList());
        } catch (Exception e) {
            throw new MainExection(CASEDELETEERROR.getCode(), CASEDELETEERROR.getMsg());
        }
        return true;
    }

    @Override
    public CaseDaoDetailInfo detail(Integer caseId) throws MainExection {
        //查询测试用例是否存在
        CaseInterface caseInterface = baseMapper.selectById(caseId);
        if (caseInterface == null) {
            throw new MainExection(CASEERROR.getCode(), CASEERROR.getMsg());
        }
        CaseDaoDto dtoByCaseId = caseDao.findDtoByCaseId(caseId);
        LambdaQueryWrapper<Params> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Params::getCaseId, caseId);
        List<Params> params = paramsDao.selectList(lambdaQueryWrapper);
        CaseDaoDetailInfo caseDaoDetailInfo = CommonUtil.copyOne(dtoByCaseId, CaseDaoDetailInfo.class);
        if (params != null && params.size() > 0) {
            caseDaoDetailInfo.setParamsList(params);
        }
        return caseDaoDetailInfo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean copyCase(CaseCopySave caseCopySave) throws MainExection {
        //对用例名称进行判断是否重复
        LambdaQueryWrapper<CaseInterface> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(CaseInterface::getCaseName, caseCopySave.getCaseName())
        .last("limit 1");
        CaseInterface one = this.getOne(queryWrapper);
        if (ObjectUtil.isNotNull(one)) {
            throw new MainExection(CASENAMEERROR.getCode(), CASENAMEERROR.getMsg());
        }
        //查询被复制的测试用例
        CaseInterface caseInterface = baseMapper.selectById(caseCopySave.getCaseId());
        if (ObjectUtil.isNull(caseInterface)) {
            throw new MainExection(CASECOPYERROR.getCode(), CASECOPYERROR.getMsg());
        }
        CaseInterface caseInterface1 = CommonUtil.copyOne(caseInterface, CaseInterface.class);
        caseInterface1.setCaseId(null);
        caseInterface1.setCaseName(caseCopySave.getCaseName());
        caseInterface1.setRemark(caseCopySave.getRemark());
        caseInterface1.setCreateTime(LocalDateTime.now());
        caseInterface1.setCreateBy(UserGetAccount.getAccountId());
        caseInterface1.setCreateName(UserGetAccount.getAccountName());
        if (baseMapper.insert(caseInterface1) > 0) {
            LambdaQueryWrapper<Params> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(Params::getCaseId, caseCopySave.getCaseId());
            //复制参数
            List<Params> params = paramsDao.selectList(lambdaQueryWrapper);
            if (ObjectUtil.isNotNull(params) && params.size() > 0) {
                List<Params> paramsList = CommonUtil.copyList(params, Params.class);
                paramsList.stream().distinct().forEach(params1 -> {
                    params1.setId(null);
                    params1.setCaseId(caseInterface1.getCaseId());
                });
                boolean b = paramsService.saveBatch(paramsList);
                if (!b){
                    throw new MainExection(CASECOPYPARAMSERROR.getCode(),CASECOPYPARAMSERROR.getMsg());
                }
                return b;
            } else {
                return true;
            }
        } else {
            return false;
        }
    }
}
